#include "mypacket.h"
#include <pcap.h>
#include "CapturePacketHeaders.h"
#include <QThread>
#include <QString>
#include <QDateTime>

//定义每个以太网包的具体信息
QString information;
QString timestamp;
QString from;
QString to;
QString length;
QString protocol;

QString getMACAddress(uint8_t *mac){
    return QString("%1:%2:%3:%4:%5:%6\n")
        .arg(mac[0],2,16,QChar('0'))
        .arg(mac[1],2,16,QChar('0'))
        .arg(mac[2],2,16,QChar('0'))
        .arg(mac[3],2,16,QChar('0'))
        .arg(mac[4],2,16,QChar('0'))
        .arg(mac[5],2,16,QChar('0'));
}

//TCP
void tcpProtocolCallBack(u_char *user, const struct pcap_pkthdr *header , const unsigned char * packet){
    MyPacket* self = reinterpret_cast<MyPacket*>(user);

//     u_int16_t tcp_source_port;          // 源端口
//     u_int16_t tcp_destination_port;     // 目的端口
//     u_int32_t tcp_sequence;             // 序列号
//     u_int32_t tcp_ack;                  // 确认位
// #ifdef WORDS_BIGENDIAN
//     u_int8_t tcp_offset:4,              // 数据偏移
//         tcp_reserved:4;                 // 保留位
// #else
//     u_int8_t tcp_reserved:4,
//         tcp_offset:4;
// #endif

//     u_int8_t tcp_flags;                  // 标志位
//     u_int16_t tcp_windows;              // 窗口大小
//     u_int16_t tcp_checksum;             // 校验和
//     u_int16_t tcp_urgent_pointer;       // 紧急指针
    //jump over the ip and ethernet fragment
    tcp_header *tcpProtocol = (tcp_header*)(packet+14+20);
    unsigned short sourcePort = ntohs(tcpProtocol->tcp_source_port);
    unsigned short destinationPort = ntohs(tcpProtocol->tcp_destination_port);
    unsigned int sequence = ntohl(tcpProtocol->tcp_sequence);
    unsigned int acknowledgement = ntohl(tcpProtocol->tcp_ack);
    unsigned short windows = ntohs(tcpProtocol->tcp_windows);
    u_int16_t checksum=ntohs(tcpProtocol->tcp_checksum);
    unsigned char flags=tcpProtocol->tcp_flags;
    int headerLength=tcpProtocol->tcp_offset*4;
    unsigned short urgentPointer = ntohs(tcpProtocol->tcp_urgent_pointer);

    information += QString("TCP header:\n");
    information += QString::asprintf("Source Port:%d\n", sourcePort);
    information += QString::asprintf("Destination Port:%d\n", destinationPort);
    information += QString::asprintf("Sequence Number:%u\n", sequence);
    information += QString::asprintf("Acknowledgement Number:%u\n", acknowledgement);
    information += QString::asprintf("Header Length:%d\n", headerLength);
    information += QString::asprintf("Reserved:%d\n", tcpProtocol->tcp_reserved);
    information += QString::asprintf("Windows:%d\n", windows);
    information += QString::asprintf("CheckSum:%#x\n", checksum);
    information += QString::asprintf("Flags:%d\n", flags);
    information += QString::asprintf("Urgent Pointer:%d\n", urgentPointer);

    switch(destinationPort){
    case 80:
        information+=QString("HTTP\n");
        break;
    case 21:
        information+=QString("FTP\n");
        break;
    case 22:
        information+=QString("SSH\n");
        break;
    case 23:
        information+=QString("Telenet\n");
        break;
    case 25:
        information += QString("SMTP\n");
        break;

    case 53:
        information += QString("DNS\n");
        break;

    case 110:
        information += QString("POP3\n");
        break;

    case 143:
        information += QString("IMAP\n");
        break;

    case 443:
        information += QString("HTTPS\n");
        break;

    case 3389:
        information += QString("RDP\n");
        break;
    }

    protocol = "TCP";
    emit self->captured(timestamp,from,to,length,protocol,information);
}

//UDP
void udpProtocolCallBack(u_char *user, const struct pcap_pkthdr *header , const unsigned char * packet){
    MyPacket* self = reinterpret_cast<MyPacket*>(user);
    // u_int16_t udp_source_port;          // 源端口
    // u_int16_t udp_destination_port;     // 目的端口
    // u_int16_t udp_length;               // 数据长度
    // u_int16_t udp_checksum;             // 校验和
    udp_header *udpProtocol = (udp_header*)(packet+14+20);

    unsigned short sourcePort = ntohs(udpProtocol->udp_source_port);
    unsigned short destinationPort = ntohs(udpProtocol->udp_destination_port);
    unsigned short udpLength = ntohs(udpProtocol->udp_length);
    u_int16_t checksum=ntohs(udpProtocol->udp_checksum);

    information += "UDP Header:\n";
    information += QString::asprintf("Source Port:%d\n", sourcePort);
    information += QString::asprintf("Destination Port:%d\n", destinationPort);
    information += QString::asprintf("Length: %d\n",udpLength);
    information += QString::asprintf("Checksum: %#x\n",checksum);

    switch(destinationPort){
    case 138:
        information += QString("NETBIOS DataGram Service\n");
        break;
    case 137:
        information += QString("NETBIOS Name Service\n");
        break;
    case 139:
        information += QString("NETBIOS Session Service\n");
        break;
    case 53:
        information += QString("name-domain server\n");
        break;
    default:
        break;
    }

    protocol = "UDP";
    emit self->captured(timestamp,from,to,length,protocol,information);
}

//ICMP
void icmpProtocolCallBack(u_char *user, const struct pcap_pkthdr *header , const unsigned char * packet){
    MyPacket* self = reinterpret_cast<MyPacket*>(user);

    // u_int8_t icmp_type;                 // 类型
    // u_int8_t icmp_code;                 // 代码
    // u_int16_t icmp_checksum;            // 校验和
    // u_int16_t icmp_identifier;          // 标识符
    // u_int16_t icmp_sequence;            // 序列号
    icmp_header *icmpProtocol = (struct icmp_header *) (packet+ 14 + 20);

    information += QString::asprintf("ICMP Type: %d \n", icmpProtocol->icmp_type);
    switch (icmpProtocol->icmp_type) {
    case 0:
        information += "(Echo Reply)\n";
        information += QString::asprintf("Code: %d\n", icmpProtocol->icmp_code);
        information += QString::asprintf("Identifier: %d\n", icmpProtocol->icmp_identifier);
        information += QString::asprintf("Sequence: %d\n", icmpProtocol->icmp_sequence);
        break;
    case 8:
        information += "(Echo Request)\n";
        information += QString::asprintf("Code: %d\n", icmpProtocol->icmp_code);
        information += QString::asprintf("Identifier: %d\n", icmpProtocol->icmp_identifier);
        information += QString::asprintf("Sequence: %d\n", icmpProtocol->icmp_sequence);
        break;
    case 3:
        information += "(Destination Unreachable)\n";
        break;
    case 4:
        information += "(Source Quench)\n";
        break;
    case 5:
        information += "(Redirect)\n";
        break;
    default:
        information += "(Unknown)\n";
        break;
    }

    information += QString::asprintf("ICMP Checksum: %d\n", ntohs(icmpProtocol->icmp_checksum));

    protocol="ICMP";
    emit self->captured(timestamp,from,to,length,protocol,information);
}

//IP callback
void ipProtocolCallBack(u_char *user, const struct pcap_pkthdr *header , const unsigned char * packet){
    qDebug()<<"ipProtocolCallBack";
    MyPacket* self = reinterpret_cast<MyPacket*>(user);

    //jump over the ethernet fragment
    ip_header* ipHeader = (ip_header*)(packet+14);
    // u_int8_t ip_tos;                // 服务类型
    // u_int16_t ip_length;            // 总长度
    // u_int16_t ip_id;                // 标识符
    // u_int16_t ip_off;               // 片偏移
    // u_int8_t ip_ttl;                // 生存时间
    // u_int8_t ip_protocol;           // 协议
    // u_int16_t ip_checksum;        // 校验和
    // struct in_addr ip_source_address;   // 源IP地址
    // struct in_addr ip_destination_address;  // 目的IP地址

    unsigned char tos = ipHeader->ip_tos;
    unsigned int headerLength = ipHeader->ip_header_length*4;
    unsigned int offset = ntohs(ipHeader->ip_off);
    u_int16_t checksum = ntohs(ipHeader->ip_checksum);
    from = QString(inet_ntoa(ipHeader->ip_source_address));
    to = QString(inet_ntoa(ipHeader->ip_destination_address));

    information+="IP Version:";
    information+=QString::asprintf("Header length:%d\n",headerLength);
    information+=QString::asprintf("TOS:%d\n",tos);
    information+=QString::asprintf("Total length:%d\n",ntohs(ipHeader->ip_length));
    information+=QString::asprintf("Identification:%d\n",ntohs(ipHeader->ip_id));
    information+=QString::asprintf("Offset:%d\n",(offset&0x1fff)*8);
    information+=QString::asprintf("TTL:%d\n",ipHeader->ip_ttl);
    information+=QString::asprintf("Protocol:%d\n",ipHeader->ip_protocol);
    information+=QString::asprintf("Checksum:%d\n",checksum);

    switch (ipHeader->ip_protocol){

    //TCP
    case 6:
        information+=QString("TCP");
        tcpProtocolCallBack(user,header,packet);
        break;
    //UDP
    case 17:
        information+=QString("UDP");
        udpProtocolCallBack(user,header,packet);
        break;
    //ICMP
    case 1:
        information+=QString("ICMP");
        icmpProtocolCallBack(user,header,packet);
        break;
    default: break;
    }

}

//arp
void arpProtocolCallBack(u_char *user, const struct pcap_pkthdr *header , const unsigned char * packet){
    MyPacket* self = reinterpret_cast<MyPacket*>(user);
    // u_int16_t arp_hardware_type;        // 硬件类型
    // u_int16_t arp_protocol_type;        // 协议类型
    // u_int8_t arp_hardware_length;       // 硬件地址长度
    // u_int8_t arp_protocol_length;       // 协议地址长度
    // u_int16_t arp_operation_code;       // 操作码
    // u_int8_t arp_source_ethernet_address[6];    // 源MAC地址
    // u_int8_t arp_source_ip_address[4];          // 源IP地址
    // u_int8_t arp_destination_ethernet_address[6];   // 目标MAC地址
    // u_int8_t arp_destination_ip_address[4];         // 目标IP地址

    arp_header *arpProtocol=(arp_header*) (packet+14);
    unsigned short protocolType=ntohs(arpProtocol->arp_protocol_type);
    unsigned short hardwareType=ntohs(arpProtocol->arp_hardware_type);
    unsigned char hardwareLength=arpProtocol->arp_hardware_length;;
    unsigned char protocolLength=arpProtocol->arp_protocol_length;
    unsigned short operationCode=ntohs(arpProtocol->arp_operation_code);
    unsigned char *mac;
    struct in_addr sourceIpAddress;
    struct in_addr destinationIpAddress;

    information+=QString("ARP Header:");
    information += QString::asprintf("ARP Hardware Type: %d\n", hardwareType);
    information += QString::asprintf("ARP Protocol Type: %d\n", protocolType);
    information += QString::asprintf("ARP Operation Code: %d\n", operationCode);
    information += QString::asprintf("ARP Hardware Length: %d\n", hardwareLength);
    information += QString::asprintf("ARP Protocol Length: %d\n", protocolLength);
    switch(operationCode){
        case 1:
            information+=QString("ARP Request Protocol\n");
        break;
        case 2:
            information+=QString("ARP Reply Protocol\n");
        break;
        case 3:
            information+=QString("RARP Request Protocol\n");
            break;
        case 4:
            information+=QString("RARP Reply Protocol\n");
        break;
        default:break;
    }
    information+=QString("Mac Source Address:");
    mac = arpProtocol->arp_source_ethernet_address;
    information+=getMACAddress(mac);
    memcpy((void*)&sourceIpAddress, (void*)&arpProtocol->arp_source_ip_address, sizeof(struct in_addr));
    information += QString::asprintf("\nIP Source Address: %s\n", inet_ntoa(sourceIpAddress));

    information+=QString("Mac Destination Address:");
    mac = arpProtocol->arp_destination_ethernet_address;
    information+=getMACAddress(mac);
    memcpy((void*)&destinationIpAddress, (void*)&arpProtocol->arp_destination_ethernet_address, sizeof(struct in_addr));
    information += QString::asprintf("\nIP Destination Address: %s\n", inet_ntoa(destinationIpAddress));


    from = QString(inet_ntoa(sourceIpAddress));
    to = QString(inet_ntoa(destinationIpAddress));

    protocol = "ARP";
    emit self->captured(timestamp,from,to,length,protocol,information);
}


//以太网协议回调函数
void MyPacket::etProtocolCallBack(u_char *user,const struct pcap_pkthdr *header,const u_char *packet){
    //将data转换为PacketCaptureThread* 对象,进行多线程处理
    MyPacket* self = reinterpret_cast<MyPacket*>(user);

    //获取以太网头，将以太网字节序转换为主机字节序
    et_header* etHeader = (et_header*)packet;
    uint16_t protocolType = ntohs(etHeader->et_type);

    //解析源mac地址
    uint8_t *macFromAddress = etHeader->et_source;
    //解析目的mac地址
    uint8_t *macToAddress = etHeader ->et_destination;
    //获取数据包长度
    length = QString::number(header->len);
    //获取数据包时间
    timestamp = QDateTime::fromMSecsSinceEpoch(
        header->ts.tv_sec*1000LL + header->ts.tv_usec/1000
                    ).toString("yyyy-MM-dd HH:mm:ss");

    //获取数据包具体信息:
    information = "";
    information += QString("MAC SOURCE: ")
                   +getMACAddress(macFromAddress);
    information += QString("MAC DESTINATION: ")
                   +getMACAddress(macToAddress);
    //判断不同协议，调用对应回调函数
    switch(protocolType) {
    case 0x0800:
        protocol = "ip";
        ipProtocolCallBack(user,header,packet);
        break;
    case 0x0806:
        protocol = "ARP";
        arpProtocolCallBack(user,header,packet);
        break;
    case 0x8035:
        protocol = "RARP";
        emit self->captured(timestamp,from,to,length,protocol,information);
        break;
    default:
        protocol = "Unknown";
        emit self->captured(timestamp,from,to,length,protocol,information);
        break;
    }
}

void printDeviceInfo(pcap_if_t* dev) {
    printf("Device: %s\n", dev->name);
    if (dev->description)
        printf("Description: %s\n", dev->description);
    printf("Addresses:\n");
    for (pcap_addr_t* addr = dev->addresses; addr != NULL; addr = addr->next) {
        if (addr->addr->sa_family == AF_INET) {
            printf("\tIP Address: %s\n",
                   inet_ntoa(((struct sockaddr_in*)addr->addr)->sin_addr));
        }
    }
}

MyPacket::MyPacket(QString context,QObject *parent):
    QThread(parent),
    context(context),
    pcapUtil(nullptr),
    isRunning(false){}


void MyPacket::run(){
    /* 定义一个错误信息缓冲区，用于存储pcap_lookupdev和pcap_lookupnet函数可能产生的错误信息 */
    char error_content[PCAP_ERRBUF_SIZE];
    //定义BF过滤器规则（空表示不过滤）
    char bpfFilterString[] ="";
    //定义网络接口的IP地址
    bpf_u_int32 netIP = PCAP_NETMASK_UNKNOWN;
    //定义bpf过滤器
    bpf_program bpfFilter;

    //转换网络接口名称，将utf-16转换为utf-8
    QByteArray ba = context.toLocal8Bit();
    const char*interface = ba.constData();

    //打开网络接口,捕获所有流量
    //the forth paramater paid me totally two hours
    pcapUtil = pcap_open_live(interface,BUFSIZ,1,1000,error_content);
    if(pcapUtil == nullptr){
        qDebug()<<"Error opening device";
        return;
    }

    //配置bpf过滤器
    pcap_compile(pcapUtil,&bpfFilter,bpfFilterString,0,netIP);
    pcap_setfilter(pcapUtil,&bpfFilter);

    //确保只监听以太网接口
    if(pcap_datalink(pcapUtil)!=DLT_EN10MB){
        qDebug()<<"We can only capture Ethernet packets";
        return;
    }

    isRunning = true;
    while(isRunning&&!isInterruptionRequested()){
        int response = pcap_dispatch(pcapUtil,1,etProtocolCallBack,
                                     reinterpret_cast<unsigned char*>(this));

        if(response == -1){
            qDebug()<<"failure when dispatching packet";
            return;
        }

        if(response == -2){
            qDebug() << "pcap_dispatch interrupted";
            break;
        }

        qDebug()<<information;
        qDebug()<<timestamp;
        qDebug()<<from;
        qDebug()<<to;
        qDebug()<<length;
        qDebug()<<protocol;

        //降低CPU占用率
        QThread::msleep(10);
    }

    pcap_close(pcapUtil);
}

void MyPacket::stop(){
    if(pcapUtil){
        pcap_breakloop(pcapUtil);
    }
    isRunning = false;
    requestInterruption();

    this->quit();
    this->wait();
}
